// threadtest.cc 
//	Simple test case for the threads assignment.
//
//	Create two threads, and have them context switch
//	back and forth between themselves by calling Thread::Yield, 
//	to illustratethe inner workings of the thread system.
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "system.h"
#include "Table.h"
#include "BoundedBuffer2.h"
#include "dllist-driver.h"
#include "dllist-analysis.h"
#include "BoundedBuffer.h"

int T=1, N=10, F=0;
// testnum is set in main.cc
int testnum = 1;

//----------------------------------------------------------------------
// SimpleThread
// 	Loop 5 times, yielding the CPU to another ready thread 
//	each iteration.
//
//	"which" is simply a number identifying the thread, for debugging
//	purposes.
//----------------------------------------------------------------------

void
SimpleThread(int which)
{
    int num;
    
    for (num = 0; num < 5; num++) {
	printf("*** thread %d looped %d times\n", which, num);
        currentThread->Yield();
    }
}

//----------------------------------------------------------------------
// ThreadTest1
// 	Set up a ping-pong between two threads, by forking a thread 
//	to call SimpleThread, and then calling SimpleThread ourselves.
//----------------------------------------------------------------------

void
ThreadTest1()
{
    DEBUG('t', "Entering ThreadTest1");

    Thread *t = new Thread("forked thread");

    t->Fork(SimpleThread, 1);
    SimpleThread(0);
}

//when F=xxx...xxx1b enable yield test
const int yieldLevel=1;
//thread num & element num for dllist test
//list for dllist test
DLList* list;
//----------------------------------------------------------------------
// ThreadFunc
// 	thread handler for dllist thread test
//----------------------------------------------------------------------

void ThreadFunc(int which)
{
    switch (which)
    {
    case 0: //generate thread
        
        MayBeYield(yieldLevel);

        GenerateRandomItemWithKeys(N,list);

        MayBeYield(yieldLevel);

        break;
    case 1: //remove thread

        MayBeYield(yieldLevel);

        RemoveItemFromHead(N,list);

        MayBeYield(yieldLevel);

        break;
    default:
        //do nothing
        break;
    }
}


char** data_read;   // data area for read threads
char** data_write;  // data area for write threads
int* bytes_read;    // the number of bytes for each read thread
int* bytes_write;   // the number of bytes for each write thread
BoundedBuffer* test_buf;    // bounded buffer

//----------------------------------------------------------------------
// BoundedBufferReadFunc
//  thread handler for read thread in bounded buffer test
//
// "num" is the read thread number
//----------------------------------------------------------------------
void BoundedBufferReadFunc(int num)
{
    test_buf->Read(data_read[num], bytes_read[num]);
}

//----------------------------------------------------------------------
// BoundedBufferWriteFunc
//  thread handler for write thread in bounded buffer test
//
// "num" is the write thread number
//----------------------------------------------------------------------
void BoundedBufferWriteFunc(int num)
{
    test_buf->Write(data_write[num], bytes_write[num]);
}

//----------------------------------------------------------------------
// DllistThreadTest
// 	test dlllistthread
//----------------------------------------------------------------------

void DllistThreadTest()
{
    DEBUG('t', "Entering DllistThreadTest");

    printf("This test is designed to show the "
     "many ways that concurrent code like dllist generate&remove "
     "can break given a nondeterministic ordering of thread "
     "executions at runtime. \n");
    printf("Dllist generate means generates N items with random "
     "keys and inserts them into a doubly-linked list.\nDllist remove "
     "means removes N items starting from the "
     "head of the list and prints out the removed items to the "
     "console. .\nNow Input "
     "the item number N (must be positive integers):");
    scanf("%d", &N);
    printf("Then the system will generate some threads "
     "(half generate, half remove) to do the jobs "
   	 "above and the threads switch randomly (3 locations "
     "optional with 50 percent probability to switch).\nPlease "
     "input the threads number T (must be even integers):");
    scanf("%d", &T);
    T = T / 2;
    printf("There are 3 granularities for thread switch:\n"
     "* thread switch occurred when N items generation or remove finished.\n"
     "* thread switch occurred when 1 item generation or remove finished.\n"
     "* thread switch occurred when the spcific option of item insert&remove.\n"
     "Please type a 3-bit binary code (each bit represent the 3 granularities "
     "above From low to high)  to see the different ways of thread switching\n"
     "(i.e. code 6 (110) means thread switch occurred both when 1 item "
     "generation or remove finished and the spcific option of item "
     "insert&remove.):\n");
    scanf("%d", &F);
    // scanf("%d %d", &T, &N);
    list=new DLList();
    for(int i=0;i<T;i++){
        char* name=new char[20];
        sprintf(name,"GENERATE-%d",i);
        Thread *t1=new Thread(name);
        
        //MayBeYield(yieldLevel);

        t1->Fork(ThreadFunc,0);
        name=new char[20];
        sprintf(name,"REMOVE-%d",i);
        Thread *t2=new Thread(name);
        
        //MayBeYield(yieldLevel);

        t2->Fork(ThreadFunc,1);
    }
}

// sych-table-test
void SynchTableTest()
{

}

// dllist-thread-analysis
void DllistThreadAnalysis()
{
	DEBUG('t', "Entering DllistThreadAnalysis");

	printf("This analysis points to the details of dllist that "
		"thread switch occurrs when item insert&remove.\n"
		"There are 4 different cases optional."
		"(Specific details in the docs).\nNow input the case number(1~4):");

	int c;
	scanf("%d", &c);

	switch(c){
		case 1:
		CaseOne();
		break;
		case 2:
		CaseTwo();
		break;
		case 3:
		CaseThree();
		break;
		case 4:
		CaseFour();
		break;
		default:
		printf("No such case.\n");
		break;
	}
}

//----------------------------------------------------------------------
// SynchBoundedBufferTest
//  Set up a test for Boundedbuffer, also a situation of bounded
//  producer/consumer probelm.
//----------------------------------------------------------------------
void SynchBoundedBufferTest()
{
    DEBUG('t', "Entering SynchBoundedBufferTest\n");

    printf("This test is designed to show the solution of  a classical"
     " synchronization problem called bounded producer/consumer.\n");


    // input the buffer size
    printf("Please input the buffer size(bytes):\n");

    int max_size;

    scanf("%d", &max_size);

    int cnt_write, cnt_read;

    // input the number of read threads
    printf("Please input the number of read threads:\n");

    scanf("%d", &cnt_read);
    bytes_read = new int[cnt_read];
    data_read = new char*[cnt_read];

    // input the number of bytes for each write thread
    printf("Please input the number of bytes for each write thread:\n");

    for (int i = 0; i < cnt_read; ++i)
    {
        scanf("%d", bytes_read + i);
        data_read[i] = new char[*(bytes_read + i)];
    }

    // input the number of write threads
    printf("Please input the number of write threads:\n");

    scanf("%d", &cnt_write);
    bytes_write = new int[cnt_write];
    data_write = new char*[cnt_write];

    // input the number of bytes for each write thread
    printf("Please input the number of bytes for each write thread:\n");

    for (int i = 0; i < cnt_write; ++i)
    {
        scanf("%d", bytes_write + i);
        data_write[i] = new char[*(bytes_write + i)];
        memset(data_write[i], '0' + i, *(bytes_write + i) * sizeof(char));
    }

    printf("The thread will register by the order of your input.\n");

    // create the buffer
    test_buf = new BoundedBuffer(max_size);

    Thread *t;
    char* thread_name_write[cnt_write];
    char* thread_name_read[cnt_read];

    for (int i = 0; i < cnt_read; ++i)
    {   
        // create the read threads
        thread_name_read[i] = new char[15];
        sprintf(thread_name_read[i], "Read-thread-%d", i);
        t = new Thread(thread_name_read[i]);
        t->Fork(BoundedBufferReadFunc, i);  
    }

    for (int i = 0; i < cnt_write; ++i)
    {
        // create the write threads
        thread_name_write[i] = new char[15];
        sprintf(thread_name_write[i], "Write-thread-%d", i);
        t = new Thread(thread_name_write[i]);
        t->Fork(BoundedBufferWriteFunc, i);
    }

}


//----------------------------------------------------------------------
//BoundedBuffer2 test area
//use for bounded-buffer2-test
//----------------------------------------------------------------------
BoundedBuffer2* BBuffer2;
void bufferProducer(int which){//Producer model for BB2
	while(true){
		int size = Random()%10+1;
		char *data = new char[size];
		for(int i=0;i<size;i++){
			data[i]=i;
		}
		BBuffer2->Write(data,size);
		currentThread->Yield();
	}
}

void bufferConsumer(int which){//Consumer model for BB2
	while(true){
		int size = Random()%10+1;
		char *data = new char[size];
		BBuffer2->Read(data,size);
		currentThread->Yield();
	}
}

void BB2modelTest(){
	DEBUG('t', "Entering BB2modelTest\n");

    printf("This test is designed to show the solution of  a classical"
     " synchronization problem called bounded producer/consumer.\n");
	 
	//input buffer's size
	printf("Please input the buffer size(bytes):\n");

    int max_size;
	scanf("%d",&max_size);
	BBuffer2 = new BoundedBuffer2(max_size);
	
	//input producer's num
	int pronum,connum;
	printf("Please input the producer's number:\n");
	scanf("%d",&pronum);
	
	//input consumer's num
	printf("Please input the consumer's number:\n");
	scanf("%d",&connum);
	
	Thread *t;
	char* thread_name_write[pronum];
    char* thread_name_read[connum];
	
	for(int i=0;i<connum;i++){//Create consumer threads
		thread_name_read[i] = new char[15];
		sprintf(thread_name_read[i],"Read-thread-%d",i);
		t = new Thread(thread_name_read[i]);
		t->Fork(bufferConsumer,0);
	}
	
	for(int i=0;i<pronum;i++){//Create producer threads
		thread_name_write[i] = new char[15];
		sprintf(thread_name_write[i],"Write-thread-%d",i);
		t = new Thread(thread_name_write[i]);
		t->Fork(bufferProducer,0);
	}
}

//----------------------------------------------------------------------
// tableTest
// 	initialize a table and run it.
//----------------------------------------------------------------------
Table* table;
void tableProducer(int which){
  while(true){
	int alpha = Random()%2;
    int *item = new int[1];
    item[0]=Random()%20;
	if(table->CurrentPoint!=table->sizereturn())
		printf("%s produced %d in %d\n",currentThread->getName(),item[0],table->Alloc((void *)item));
	if(alpha==0)
		currentThread->Yield();
  }
}
void tableConsumer(int which){
    while(true){
		int alpha = Random()%2;
		void *point = NULL;
		if(table->CurrentPoint != 0)
			point=table->Get(table->CurrentPoint-1);
		if(point!=NULL){
			printf("%s comsumed %d ",currentThread->getName(),*((int *)point));
			printf("in %d\n",table->CurrentPoint);
			table->Release(table->CurrentPoint);
        }
		if(alpha==0)
			currentThread->Yield();
    }
}
void tableTestOrigin(int tablesize,int ProducerNum,int ConsumerNum){
    DEBUG('t',"Debugging the Table");
	table = new Table(tablesize);
    //producer threads
    for(int i = 0;i<ProducerNum;i++){
        char *thread_name_write = new char[20];
        sprintf(thread_name_write,"Write-thread-%d",i);
        Thread *t = new Thread(thread_name_write);
        t->Fork(tableProducer,0);
	}
    //consumer threads
    for(int i = 0;i<ConsumerNum;i++){
        char *thread_name_read = new char[20];
        sprintf(thread_name_read,"Read-thread-%d",i);
        Thread *t = new Thread(thread_name_read);
        t->Fork(tableConsumer,0);
    }
}
// use for other space
void tableTest(){
    int tablesize,ProducerNum,ConsumerNum;
    printf("plz tell me tablesize =");
    scanf("%d",&tablesize);
	printf("plz tell me producernum =");
    scanf("%d",&ProducerNum);
	printf("plz tell me consumernum =");
    scanf("%d",&ConsumerNum);
    tableTestOrigin(tablesize,ProducerNum,ConsumerNum);
}

//----------------------------------------------------------------------
// ThreadTest
// 	Invoke a test routine.
//----------------------------------------------------------------------
void
ThreadTest()
{
    switch (testnum) {
    case 1:
	ThreadTest1();
	break;
    case 2:
    DllistThreadTest();
    break;
	case 3:
	DllistThreadAnalysis();
	break;
    case 4:
    SynchTableTest();
    break;
    case 5:
    SynchBoundedBufferTest();
    break;
	case 6:
	BB2modelTest();
	break;
	case 7:
	tableTest();
    default:
	printf("No test specified.\n");
	break;
    }
}
